package org.atlantis.uzw.base.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisConnectionException;

public class RedisTemplate implements DisposableBean {

	private static Logger log = LoggerFactory.getLogger(RedisTemplate.class);

	private JedisPool jedisPool;

	public void setJedisPool(JedisPool jedisPool) {
		this.jedisPool = jedisPool;
	}

	public <T> T execute(RedisAction<T> redisAction) {
		Jedis jedis = null;
		boolean broken = false;
		try {
			jedis = jedisPool.getResource();
			return redisAction.action(jedis);
		} catch (JedisConnectionException e) {
			log.error("Redis connection lost.", e);
			broken = true;
			throw e;
		} finally {
			closeResource(jedis, broken);
		}
	}

	public void execute(RedisActionNoResult redisActionNoResult) {
		Jedis jedis = null;
		boolean broken = false;
		try {
			jedis = jedisPool.getResource();
			redisActionNoResult.action(jedis);
		} catch (JedisConnectionException e) {
			log.error("Redis connection lost.", e);
			broken = true;
			throw e;
		} finally {
			closeResource(jedis, broken);
		}
	}

	public JedisPool getJedisPool() {
		return jedisPool;
	}

	protected void closeResource(Jedis jedis, boolean connectionBroken) {
		if (jedis == null) return;
		if (connectionBroken) jedisPool.returnBrokenResource(jedis);
		else jedisPool.returnResource(jedis);
	}

	public static interface RedisAction<T> {
		T action(Jedis jedis);
	}

	public static interface RedisActionNoResult {
		void action(Jedis jedis);
	}

	public boolean del(final String key) {
		return execute(new RedisAction<Boolean>() {
			@Override
			public Boolean action(Jedis jedis) {
				return jedis.del(key) == 1;
			}
		});
	}

	public void flushDB() {
		execute(new RedisActionNoResult() {
			@Override
			public void action(Jedis jedis) {
				jedis.flushDB();
			}
		});
	}

	public String get(final String key) {
		return execute(new RedisAction<String>() {
			@Override
			public String action(Jedis jedis) {
				return jedis.get(key);
			}
		});
	}

	public Long getAsLong(final String key) {
		String result = get(key);
		return result == null ? Long.valueOf(result) : 0;
	}

	public Integer getAsInt(final String key) {
		String result = get(key);
		return result != null ? Integer.valueOf(result) : 0;
	}

	public void set(final String key, final String value) {
		execute(new RedisActionNoResult() {
			@Override
			public void action(Jedis jedis) {
				jedis.set(key, value);
			}
		});
	}

	public void setex(final String key, final String value, final int seconds) {
		execute(new RedisActionNoResult() {
			@Override
			public void action(Jedis jedis) {
				jedis.setex(key, seconds, value);
			}
		});
	}

	public boolean setnx(final String key, final String value) {
		return execute(new RedisAction<Boolean>() {
			@Override
			public Boolean action(Jedis jedis) {
				return jedis.setnx(key, value) == 1 ? true : false;
			}
		});
	}

	public long incr(final String key) {
		return execute(new RedisAction<Long>() {
			@Override
			public Long action(Jedis jedis) {
				return jedis.incr(key);
			}
		});
	}

	public long decr(final String key) {
		return execute(new RedisAction<Long>() {
			@Override
			public Long action(Jedis jedis) {
				return jedis.decr(key);
			}
		});
	}

	public void lpush(final String key, final String value) {
		execute(new RedisActionNoResult() {
			@Override
			public void action(Jedis jedis) {
				jedis.lpush(key, value);
			}
		});
	}

	public long llen(final String key) {
		return execute(new RedisAction<Long>() {
			@Override
			public Long action(Jedis jedis) {
				return jedis.llen(key);
			}
		});
	}

	public boolean zadd(final String key, final String member, final double score) {
		return execute(new RedisAction<Boolean>() {
			@Override
			public Boolean action(Jedis jedis) {
				return jedis.zadd(key, score, member) == 1 ? true : false;
			}
		});
	}

	public boolean zrem(final String key, final String member) {
		return execute(new RedisAction<Boolean>() {
			@Override
			public Boolean action(Jedis jedis) {
				return jedis.zrem(key, member) == 1 ? true : false;
			}
		});
	}

	public long zcard(final String key) {
		return execute(new RedisAction<Long>() {
			@Override
			public Long action(Jedis jedis) {
				return jedis.zcard(key);
			}
		});
	}

	@Override
	public void destroy() throws Exception {
		if (jedisPool != null) jedisPool.destroy();
	}
}
